import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;

import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.NewImage;
import ij.process.ImageProcessor;

/**
*
* Bachelor thesis at the Institute of Biomedical Engineering, 
* located at the UMIT/EWZ campus in Hall in Tyrol. <br><br>
*
* This class is responsible for creating the myocardium after the segmention
* of the bloodmasses.
* 
* @author Stefan Fuchs
*
*/
public class RollingBallOperator implements Runnable, ActionListener,
		WindowListener {

	/** Percentage of voxels larger then the max border*/
	private static final double OUTER_BOARDER_MAX_MYOCARD = 0.95d;
	
	/** Percentage of voxels for the correct thickness*/
	private static final double MYOCARD_BORDER = 0.97d;
	// /////////////////////////////////////////////
	
	// int
	/** Height of the Image */
	private int height;

	/** Width of the Image */
	private int width;

	/** Amount of Slices in the stack */
	private int size;
	
	/** Minimal roi parameter, X-axis. */
	private int minRoiX;

	/** Maximal roi parameter, X-axis. */
	private int maxRoiX;

	/** Minimal roi parameter, Y-axis. */
	private int minRoiY;

	/** Maximal roi parameter, Y-axis. */
	private int maxRoiY;

	/** Minimal roi parameter, Z-axis. */
	private int minRoiZ;

	/** Maximal roi parameter, Z-axis */
	private int maxRoiZ;

	/** Thickness of the myocardium containing bloodmass 1 */
	private int averageBloodMassOneThickness;
	
	/** Minimum thickness of myocardium around bloodmass 1*/
	private int minBloodMassOneThickness;
	
	/** Maximum thickness of myocardium around bloodmass 1*/
	private int maxBloodMassOneThickness;

	/** Thickness of the mycardium containing bloodmass 2 */
	private int averageBloodMassTwoThickness;
	
	/** Minimum thickness of myocardium around bloodmass 2*/
	private int minBloodMassTwoThickness;
	
	/** Maximum thickness of myocardium around bloodmass 2*/
	private int maxBloodMassTwoThickness;
	

	/** Sequence counter for generating image titles. */
	private int seqCounter;

	/** The minimum gray-value of the myocard */
	private int minGrayValue;

	/** The maximum gray-value of the myocard */
	private int maxGrayValue;

	/** Amout of slices to consider in +- Z-direction*/
	private int slicesToConsider;
	
	// int[]
	/** The ConnectedComponentLabel from the segmentation or ASF */
	private int[] connectedComponentLabel = null;

	/** Label with the myocardium */
	private int[] myocardiumLabel = null;

	/** Temporary Label for the rolling ball */
	private int[] tmpLabel = null;

	// boolean
	private boolean running = false;

	// String
	/** Name of current image. */
	private String imageName;
	
	/** The full Image Title*/
	private String fullImageTitle = null;

	// general objects

	/** Singleton instance */
	private static RollingBallOperator instance = null;

	/** The Rolling-ball thread */
	private Thread rollingBallThread;

	/** Statusdialog */
	private StatusDialog sd;

	/** The Modell */
	private Modell modell;

	/** Contains the expanded bloodmasses */
	private ArrayList<Voxel> expandedBloodmasses = null;

	/** Grayvalue Imagestack */
	private ImageStack grayValueStack = null;

	public static synchronized RollingBallOperator getInstance() {

		if (instance == null) {
			instance = new RollingBallOperator();
		}

		return instance;
	}

	public void prepearRollingBallOperator() {
		this.running = true;
		this.rollingBallThread = new Thread(RollingBallOperator.getInstance());
		this.rollingBallThread.start();
	}

	/**
	 * Private singleton constructor
	 */
	private RollingBallOperator() {
		// dummy constructor
	}

	@Override
	public void run() {
		
		long start = System.currentTimeMillis();
		this.modell = Modell.getInstance();
		
		getDimensionParameters();

		this.sd = new StatusDialog();
		this.sd.getBtnAbort().addActionListener(this);
		this.sd.getJfDialog().addWindowListener(this);

		// Prepears the Bloodmasses for RollingBall operation
		prepearBloodMassesForRollingBall();

		startRollingBallOperation();
		getRollingBallResults();

		visualizeRollingBallResults(" Rolling Ball");

		this.sd.close();
		this.running = false;
		this.modell.setMyocardiumLabel(Util.copy1DVoxelArry(this.myocardiumLabel));
		
		long stop = System.currentTimeMillis();
		long time = stop - start;
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		System.out.println("RollingBall Duration: " + sdf.format(time));
		
		clearAll();
	}

	/**
	 * Adds the margin specified for each bloodmass and writes the label into an
	 * own array
	 */
	private void prepearBloodMassesForRollingBall() {
		this.sd.setTitle("Rolling Ball Operation");
		this.sd.setTime("");
		this.sd.show(true);
		
		this.averageBloodMassOneThickness = (int) Math.round(this.averageBloodMassOneThickness / this.modell.getVoxelWidth());
		this.averageBloodMassTwoThickness = (int) Math.round(this.averageBloodMassTwoThickness /this.modell.getVoxelWidth());
		
		this.minBloodMassOneThickness = (int) Math.round(this.minBloodMassOneThickness/this.modell.getVoxelWidth());
		this.maxBloodMassOneThickness = (int) Math.round(this.maxBloodMassOneThickness/this.modell.getVoxelWidth());
		
		this.minBloodMassTwoThickness = (int) Math.round(this.minBloodMassTwoThickness/this.modell.getVoxelWidth());
		this.maxBloodMassTwoThickness = (int) Math.round(this.maxBloodMassTwoThickness/this.modell.getVoxelWidth());

		this.connectedComponentLabel = this.modell.getAlternatingSequenceFilteringLabel();
		this.grayValueStack = this.modell.getGrayValueImageStack();

		this.myocardiumLabel = new int[this.height * this.width * this.size];
		this.tmpLabel = new int[this.height * this.width * this.size];

		int coordinate1D;

		// initialize the labels with 0
		for (int z = 0; z < this.size; z++) {
			this.sd.setStatus("Initializing done: "	+ (100 * z / (this.size - 1)) + " %");
			for (int y = 0; y < this.height; y++) {
				for (int x = 0; x < this.width; x++) {
					coordinate1D = (int) (this.width * (this.height * z + y) + x);

					this.tmpLabel[coordinate1D] = 0;
					this.myocardiumLabel[coordinate1D] = 0;

				}
			}
		}
	}

	/**
	 * Calculates the myocard for every boundary voxel
	 * 
	 * @param x
	 * @param y
	 * @param z
	 */
	private void computeGrowVoxel(int x, int y, int z) {

		int coordiante1D = (int) (this.width * (this.height * z + y) + x);
		int tmpCoordinate1D;
		
		int depth = 0;
		int voxelCtr = 0;
		int myocardVoxelCtr = 0;
		int[] voxelValue = null;
		double quotient = 0d;
		int newRadius = 0;
		int zz = 0;
		
		
		//If wer are in bloodmass 1
		if ( this.connectedComponentLabel[coordiante1D] == 1 ) {
			
			for (int i = this.maxBloodMassOneThickness + 1 ; i > 0 ; i--) {
				voxelCtr = 0;
				myocardVoxelCtr = 0;
				depth = i;
				for ( int zzz = - this.slicesToConsider ; zzz <= this.slicesToConsider ; zzz++) {
					
					newRadius = (int)(Math.sqrt(Math.pow(i, 2) - Math.pow(zzz, 2)));
					
					for ( int yy = y - newRadius ; yy <= y + newRadius ; yy++) {
						for ( int xx = x - newRadius ; xx <= x + newRadius ; xx++) {
							
							zz = z + zzz;
							
							if (xx < 0 || xx >= this.width || yy < 0 || yy >= this.height || zz < 0 || zz >= this.maxRoiZ ) {
								continue;
							}
						
							tmpCoordinate1D = (int) ( this.width * ( this.height * zz + yy ) + xx);

							//Not in the bloodmass
							if ( this.connectedComponentLabel[tmpCoordinate1D] != 1 && this.connectedComponentLabel[tmpCoordinate1D] != 2 ) {

								voxelCtr++;
			
								voxelValue = this.grayValueStack.getProcessor(zz+1).getPixel(xx, yy, voxelValue);
								//We found myocard with the appropriate grayvalue
								if ( voxelValue[0] >= this.minGrayValue && voxelValue[0] <= this.maxGrayValue ) {
								
									myocardVoxelCtr++;
								}
							} 
						}
					}
				}
				
				//check if there there is myocard larger than the maxvalue
				if ( depth == ( this.maxBloodMassOneThickness + 1 ) ) {
					
					quotient = (double) myocardVoxelCtr / (double) voxelCtr ;
					//draw 
					if ( quotient >= OUTER_BOARDER_MAX_MYOCARD) {
						break;
					}
				} else {
					
					quotient = (double) myocardVoxelCtr / (double) voxelCtr ;
					if ( quotient >= MYOCARD_BORDER ) {
						break;
					}
				}
			}
			
			//If the thickness is more than the maximum or less than the minimum thickness, its the average thickness
			if ( depth == ( this.maxBloodMassOneThickness + 1 ) || depth < this.minBloodMassOneThickness) {
				for ( int zzz = - this.averageBloodMassOneThickness ; zzz <= this.averageBloodMassOneThickness; zzz++) {
					
					newRadius = (int)(Math.sqrt(Math.pow(this.averageBloodMassOneThickness, 2) - Math.pow(zzz, 2)));
					
					for ( int yy = y - newRadius; yy <= y + newRadius ;yy++) {
						for ( int xx = x - newRadius; xx <= x + newRadius;xx++ ) {
				
							zz = z + zzz;
							
							
							if (xx < 0 || xx >= this.width || yy < 0 || yy >= this.height || zz < 0 || zz >= this.maxRoiZ ) {
								continue;
							}
						
							tmpCoordinate1D = (int) ( this.width * ( this.height * zz + yy ) + xx);
						
							if ( this.connectedComponentLabel[tmpCoordinate1D] != 1 && this.connectedComponentLabel[tmpCoordinate1D] != 2 ) {
							
								//this.myocardiumLabel[tmpCoordinate1D] = 1;
								setMyocardiumVoxel(xx, yy, zz, 1);
							}
						}
					}
				}
			} else {
				for ( int zzz = - depth ; zzz <= depth; zzz++) {
					
					newRadius = (int)(Math.sqrt(Math.pow(depth, 2) - Math.pow(zzz, 2)));
					
					for ( int yy = y - newRadius ; yy <= y + newRadius ; yy++) {
						for ( int xx = x - newRadius; xx <= x + newRadius ; xx++ ) {
						
							zz = z + zzz;
							
							if (xx < 0 || xx >= this.width || yy < 0 || yy >= this.height || zz < 0 || zz >= this.maxRoiZ ) {
								continue;
							}
							
							tmpCoordinate1D = (int) ( this.width * ( this.height * zz + yy ) + xx);
						
							if ( this.connectedComponentLabel[tmpCoordinate1D] != 1 && this.connectedComponentLabel[tmpCoordinate1D] != 2 ) {
							
								setMyocardiumVoxel(xx, yy, zz, 1);
							}
						}
					}
				}
			}
		}

		if ( this.connectedComponentLabel[coordiante1D] == 2 ) {
			
			for (int i = this.maxBloodMassTwoThickness + 1 ; i > 0 ; i--) {
				voxelCtr = 0;
				myocardVoxelCtr = 0;
				depth = i;
				for ( int zzz = - this.slicesToConsider ; zzz <= this.slicesToConsider ; zzz++ ) {
					
					newRadius = (int)(Math.sqrt(Math.pow(i, 2) - Math.pow(zzz, 2)));
					
					for ( int yy = y - newRadius ; yy <= y + newRadius ; yy++ ) {
						for ( int xx = x - newRadius ; xx <= x + newRadius ; xx++ ) {
							
							zz = z + zzz;
						
							if (xx < 0 || xx >= this.width || yy < 0 || yy >= this.height || zz < 0 || zz >= this.maxRoiZ ) {
								continue;
							}
							
							tmpCoordinate1D = (int) ( this.width * ( this.height * zz + yy ) + xx);

							//	Not in the bloodmass
							if ( this.connectedComponentLabel[tmpCoordinate1D] != 1 && this.connectedComponentLabel[tmpCoordinate1D] != 2 ) {

								voxelCtr++;
			
								voxelValue = this.grayValueStack.getProcessor(zz+1).getPixel(xx, yy, voxelValue);
								//We found myocard with the appropriate grayvalue
								if ( voxelValue[0] >= this.minGrayValue && voxelValue[0] <= this.maxGrayValue ) {
									myocardVoxelCtr++;
								}
							} 
						}
					}
				}
				
				//check if there there is myocard larger than the maxvalue
				if ( depth == ( this.maxBloodMassTwoThickness + 1 ) ) {
					
					quotient = (double) myocardVoxelCtr / (double) voxelCtr ;
					//draw 
					if ( quotient >= OUTER_BOARDER_MAX_MYOCARD) {
						break;
					}
				} else {
					
					quotient = (double) myocardVoxelCtr / (double) voxelCtr ;
					if ( quotient >= MYOCARD_BORDER ) {
						break;
					}
					
				}
			}

			//If the thickness is more than the maximum or less than the minimum thickness, its the average thickness
			if ( depth == ( this.maxBloodMassTwoThickness + 1 ) || depth < this.minBloodMassTwoThickness ) {
				
				for ( int zzz = - this.averageBloodMassTwoThickness ; zzz <= this.averageBloodMassTwoThickness ; zzz++ ) {
					
					newRadius = (int)(Math.sqrt(Math.pow(this.averageBloodMassTwoThickness, 2) - Math.pow(zzz, 2)));
					
					for ( int yy = y - newRadius ; yy <= y + newRadius; yy++) {
						for ( int xx = x - newRadius; xx <= x + newRadius; xx++ ) {
						
							zz = z + zzz;
							
							if (xx < 0 || xx >= this.width || yy < 0 || yy >= this.height || zz < 0 || zz >= this.maxRoiZ ) {
								continue;
							}
						
							tmpCoordinate1D = (int) ( this.width * ( this.height * zz + yy ) + xx);
						
							if ( this.connectedComponentLabel[tmpCoordinate1D] != 1 && this.connectedComponentLabel[tmpCoordinate1D] != 2 ) {
								setMyocardiumVoxel(xx, yy, zz, 2);
							}
						}
					}
				}
			} else {
				for ( int zzz = - depth ; zzz <= depth ; zzz++ ) {
					
					newRadius = (int)(Math.sqrt(Math.pow(depth, 2) - Math.pow(zzz, 2)));
					
					for ( int yy = y - newRadius ; yy <= y + newRadius ; yy++ ) {
						for ( int xx = x - newRadius; xx <= x + newRadius ; xx++ ) {

							zz = z + zzz;
							
							if (xx < 0 || xx >= this.width || yy < 0 || yy >= this.height || zz < 0 || zz >= this.maxRoiZ ) {
								continue;
							}
							
							tmpCoordinate1D = (int) ( this.width * ( this.height * zz + yy ) + xx);
						
							if ( this.connectedComponentLabel[tmpCoordinate1D] != 1 && this.connectedComponentLabel[tmpCoordinate1D] != 2 ) {
							
								setMyocardiumVoxel(xx, yy, zz, 2);
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Sets the myocardium label
	 * 
	 * @param x
	 * @param y
	 * @param z
	 * @param labelValue
	 */
	private void setMyocardiumVoxel(int x, int y, int z, int labelValue) {

		int coordiante1D = (int) (this.width * (this.height * z + y) + x);

		if (this.myocardiumLabel[coordiante1D] == 0) {
			this.myocardiumLabel[coordiante1D] = labelValue;
		} else if (this.myocardiumLabel[coordiante1D] == 1 && labelValue == 2) {
			this.myocardiumLabel[coordiante1D] = 3;
		} else if (this.myocardiumLabel[coordiante1D] == 2 && labelValue == 1) {
			this.myocardiumLabel[coordiante1D] = 3;
		}
	}

	/**
	 * Determins if the voxel is endocard
	 * 
	 * @param x
	 * @param y
	 * @param z
	 * @return
	 */
	private boolean isBoundary(int x, int y, int z) {

		int coordinate1D = (int) (this.width * (this.height * z + y) + x);
		int tmpCoordinate1D;

		for (int zz = z - 1; zz <= z + 1; zz++) {
			for (int yy = y - 1; yy <= y + 1; yy++) {
				for (int xx = x - 1; xx <= x + 1; xx++) {

					if (xx < 0 || xx >= this.width || yy < 0
							|| yy >= this.height || zz < 0 || zz >= this.maxRoiZ) {
						continue;
					}

					tmpCoordinate1D = (int) (this.width
							* (this.height * zz + yy) + xx);

					// Look if label is a boundary (aka endcard)
					if (this.connectedComponentLabel[coordinate1D] != this.connectedComponentLabel[tmpCoordinate1D]) {
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * Starts the rolling ball operation with
	 */
	private void startRollingBallOperation() {
		int coordinate1D;

		int ctr = 0;
		
		for (int z = this.minRoiZ ; z < this.maxRoiZ; z++) {
			for (int y = this.minRoiY; y < this.maxRoiY; y++) {
				for (int x = this.minRoiX; x < this.maxRoiX; x++) {
					
					this.sd.setStatus("Rolling Ball: "	+ ( 100 * ( (this.maxRoiX - this.minRoiX ) * ( (this.maxRoiY - this.minRoiY) * ( z - this.minRoiZ )+ ( y - this.minRoiY)  ) + ( x - this.minRoiX )  ) / ((this.maxRoiZ - this.minRoiZ + 1 ) * (this.maxRoiY - this.minRoiY + 1 ) * (this.maxRoiX - this.minRoiX +1 ))) + " %");
					ctr++;
					coordinate1D = (int) (this.width * (this.height * z + y) + x);

					if (this.connectedComponentLabel[coordinate1D] == 1) {
						if (isBoundary(x, y, z)) {
							computeGrowVoxel(x, y, z);

						}
					}

					if (this.connectedComponentLabel[coordinate1D] == 2) {
						if (isBoundary(x, y, z)) {
							computeGrowVoxel(x, y, z);

						}
					}

				}
			}
		}
		System.out.println("Amount of voxels: " + ctr);
	}

	/**
	 * Get the Results and wirte them into an array
	 */
	private void getRollingBallResults() {
		this.expandedBloodmasses = new ArrayList<Voxel>();
		int coordinate1D;

		for (int z = 0; z <= this.maxRoiZ; z++) {
			this.sd.setStatus("Writing Results done: "+ (100 * z / (this.size - 1)) + " %");
			for (int y = 0; y < this.height; y++) {
				for (int x = 0; x < this.width; x++) {

					coordinate1D = (int) (this.width * (this.height * z + y) + x);

					if (this.myocardiumLabel[coordinate1D] == 1) {
						this.expandedBloodmasses.add(new Voxel(x, y, (z + 1),
								Modell.VENTRICLE_A_POINTS_LABEL.getRGB()));
					}

					if (this.myocardiumLabel[coordinate1D] == 2) {
						this.expandedBloodmasses.add(new Voxel(x, y, (z + 1),
								Modell.VENTRICLE_B_POINTS_LABEL.getRGB()));
					}

					if (this.myocardiumLabel[coordinate1D] == 3) {
						this.expandedBloodmasses.add(new Voxel(x, y, (z + 1),
								Modell.OVERLAPPING_MYOCARD_LABEL.getRGB()));
					}
				}
			}
		}
	}

	/**
	 * Visualize the results of the rolling ball operation
	 * 
	 * @param title
	 */
	private void visualizeRollingBallResults(String title) {

		this.sd.setStatus("Visualizing Rolling Ball Result...");
		String counter;

		/*
		 * Create postfix number
		 */
		if (this.seqCounter <= 9) {
			counter = "00" + this.seqCounter;
		} else if (this.seqCounter > 9 && this.seqCounter <= 99) {
			counter = "0" + this.seqCounter;
		} else {
			counter = "" + this.seqCounter;
		}
		// Close Image if its opened
		if (ij.WindowManager.getImage(this.imageName + title + "[" + counter
				+ "]") != null) {
			if (ij.WindowManager.getImage(
					this.imageName + title + "[" + counter + "]").isVisible()) {
				ij.WindowManager.getImage(this.imageName + title + "["
						+ counter + "]").changes = false;
				ij.WindowManager.getImage(
						this.imageName + title + "[" + counter + "]").close();
			}
		}
		
		this.fullImageTitle = this.imageName + title + "[" + counter + "]";

		ImagePlus rollingBallImage = new ImagePlus(this.imageName + title,
				this.modell.getImageStack());
		ImageProcessor rollingBallImageProcessor = rollingBallImage
				.getProcessor();

		for (Voxel v : this.expandedBloodmasses) {
			rollingBallImage.setSlice(v.getZ());
			rollingBallImageProcessor
					.putPixel(v.getX(), v.getY(), v.getValue());
		}

		rollingBallImage.setTitle(rollingBallImage.getTitle() + "[" + counter
				+ "]");
		rollingBallImage.setSlice(1);
		rollingBallImage.show();
	}
	
	public ImagePlus createImageSequence() {
		
		ImagePlus imageSeq;
		
		imageSeq = NewImage.createByteImage(this.imageName + "RollingBall Result", this.width, this.height, this.size, NewImage.FILL_WHITE);
		
		ImageProcessor imageProc = imageSeq.getProcessor();
		
		for ( Voxel v : this.expandedBloodmasses ) {
			
			imageSeq.setSlice(v.getZ());
			
			if ( v.getValue() == Modell.VENTRICLE_A_POINTS_LABEL.getRGB() ) {
				imageProc.putPixel(v.getX(), v.getY(), 1);
			}
			
			if ( v.getValue() == Modell.VENTRICLE_B_POINTS_LABEL.getRGB() ) {
				imageProc.putPixel(v.getX(), v.getY(), 2);
			}
			
			if ( v.getValue() == Modell.OVERLAPPING_MYOCARD_LABEL.getRGB() ) {
				imageProc.putPixel(v.getX(), v.getY(), 3);
			}
			
		}
		return imageSeq;
	}

	/**
	 * Clears all memory expensive variables and calls the GC.
	 */
	private void clearAll() {
		this.connectedComponentLabel = null;
	//	this.expandedBloodmasses = null;
		this.myocardiumLabel = null;
		this.sd = null;

		this.modell = null;

		IJ.freeMemory();
		System.gc();
	}

	/**
	 * Reads the dimension-Parameters from the model and sets them accordingly
	 */
	private void getDimensionParameters() {
		this.width = this.modell.getWidth();
		this.height = this.modell.getHeight();
		this.size = this.modell.getSize();
		this.minRoiX = this.modell.getMinRoiX();
		this.maxRoiX = this.modell.getMaxRoiX();
		this.minRoiY = this.modell.getMinRoiY();
		this.maxRoiY = this.modell.getMaxRoiY();
		this.minRoiZ = this.modell.getMinRoiZ();
		this.maxRoiZ = this.modell.getMaxRoiZ();
	}

	// //////////////////////////////
	// //// Setter & Getter /////////
	// //////////////////////////////

	/**
	 * Set Image name
	 * 
	 * @param imageName
	 *            Image name
	 */
	public void setImageName(String imageName) {
		this.imageName = imageName;
	}
	
	/**
	 * Returns the full image title
	 * 
	 * @return
	 */
	public String getFullImageTitle() {
		return fullImageTitle;
	}

	/**
	 * Indicator if the thread is running
	 * 
	 * @return
	 */
	public boolean isRunning() {
		return running;
	}

	/**
	 * Average thickness of the myocardium around bloodmass 1
	 * 
	 * @param labelOneThickness
	 */
	public void setLabelOneThickness(int labelOneThickness) {
		this.averageBloodMassOneThickness = labelOneThickness;
	}

	/**
	 * Average thickness of the mycardium around bloodmass 2
	 * 
	 * @param labelTwoThickness
	 */
	public void setLabelTwoThickness(int labelTwoThickness) {
		this.averageBloodMassTwoThickness = labelTwoThickness;
	}

	/**
	 * Sets the sequencecounter for the imagename
	 * 
	 * @param seqCounter
	 */
	public void setSeqCounter(int seqCounter) {
		this.seqCounter = seqCounter;
	}

	/**
	 * Sets the minimum gray value of the myocard
	 * 
	 * @param minGrayValue
	 */
	public void setMinGrayValue(int minGrayValue) {
		this.minGrayValue = minGrayValue;
	}

	/**
	 * Sets the maximum gray value of the myocard
	 * 
	 * @param maxGrayValue
	 */
	public void setMaxGrayValue(int maxGrayValue) {
		this.maxGrayValue = maxGrayValue;
	}
	
	/**
	 * Sets the minimum thickness of myocardium around bloodmass 1
	 * 
	 * @param minBloodMassOneThickness
	 */
	public void setMinBloodMassOneThickness(int minBloodMassOneThickness) {
		this.minBloodMassOneThickness = minBloodMassOneThickness;
	}
	
	/**
	 * Sets the maximum thickness of myocardium around bloodmass 1
	 * 
	 * @param maxBloodMassOneThickness
	 */
	public void setMaxBloodMassOneThickness(int maxBloodMassOneThickness) {
		this.maxBloodMassOneThickness = maxBloodMassOneThickness;
	}
	
	/**
	 * Sets the minimum thickness of myocardium around bloodmass 2
	 * 
	 * @param minBloodMassTwoThickness
	 */
	public void setMinBloodMassTwoThickness(int minBloodMassTwoThickness) {
		this.minBloodMassTwoThickness = minBloodMassTwoThickness;
	}
	
	/**
	 * Sets the maximum thickness of myocardium around bloodmass 2
	 * 
	 * @param maxBloodMassTwoThickness
	 */
	public void setMaxBloodMassTwoThickness(int maxBloodMassTwoThickness) {
		this.maxBloodMassTwoThickness = maxBloodMassTwoThickness;
	}
	
	/**
	 * Sets the amount of slices to be considered in Z-direction
	 * 
	 * @param slicesToConsider
	 */
	public void setSlicesToConsider(int slicesToConsider) {
		this.slicesToConsider = slicesToConsider;
	}

	// ////////////////////////////////////////
	// /////// Actionlistener /////////////////
	// ////////////////////////////////////////

	@SuppressWarnings("deprecation")
	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == this.sd.getBtnAbort()) {
			synchronized (this.rollingBallThread) {

				System.gc();
				this.rollingBallThread.stop();
			}
			this.running = false;
			this.sd.close();
		}

	}

	// ////////////////////////////////////////
	// /////// Windowlistener /////////////////
	// ////////////////////////////////////////

	@Override
	public void windowActivated(WindowEvent e) {
		// empty block
	}

	@Override
	public void windowClosed(WindowEvent e) {
		// empty block
	}

	@SuppressWarnings("deprecation")
	@Override
	public void windowClosing(WindowEvent e) {
		if (e.getSource() == this.sd.getBtnAbort()) {
			synchronized (this.rollingBallThread) {

				System.gc();
				this.rollingBallThread.stop();
			}
			this.running = false;
			this.sd.close();
		}

	}

	@Override
	public void windowDeactivated(WindowEvent e) {
		// empty block
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
		// empty block
	}

	@Override
	public void windowIconified(WindowEvent e) {
		// empty block
	}

	@Override
	public void windowOpened(WindowEvent e) {
		// empty block
	}
}